package com.cloud.tsp.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import com.alicp.jetcache.Cache;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloud.api.domain.command.CommandBatch;
import com.cloud.api.domain.custom.*;
import com.cloud.api.domain.model.ModelCommand;
import com.cloud.api.domain.tsp.*;
import com.cloud.api.myenum.CmdRelateEnum;
import com.cloud.api.myenum.ExecuteStatus;
import com.cloud.api.utils.WebHttpApi;
import com.cloud.core.exception.MyException;
import com.cloud.mybatis.config.LoginHolder;
import com.cloud.mybatis.utils.ServletUtils;
import com.cloud.rec.msg.CmdObj;
import com.cloud.rec.service.ICmdDownService;
import com.cloud.rec.util.CheckSumUtil;
import com.cloud.tsp.mapper.CommandMapper;
import com.cloud.tsp.mapper.VehicleMessageDailyMapper;
import com.cloud.tsp.service.*;
import com.fasterxml.jackson.databind.JsonNode;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.rpc.RpcContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.stream.function.StreamBridge;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author haohaibo
 * @since 2023-11-29
 */
@Service("commandService")
@Slf4j
public class CommandServiceImpl extends ServiceImpl<CommandMapper, Command> implements ICommandService {

    @Resource
    private CommandMapper commandMapper;
    @Resource
    private ICommandLogService commandLogService;
    @Resource
    private IVehicleSaleService vehicleSaleService;
    @Resource
    private ICommandExecHistoryService commandExecHistoryService;
    @Resource
    private IUserExtendService userExtendService;
    @Autowired
    private StreamBridge streamBridge;
    @Resource(name = "myRedis")
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private SymmetricCrypto symmetricCrypto;
    @DubboReference
    private ICmdDownService cmdDownService;
    @Resource
    private Cache<String, String> vehTagCache;
    @Value("${sms.switch:true}")
    private boolean smsSwitch;
    @Resource
    private VehicleMessageDailyMapper vehicleMessageDailyMapper;

    @Override
    public List<Command> selectCommand(String id) {
        LambdaQueryWrapper<Command> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Command::getType,"admin");
        VehicleSale byId = vehicleSaleService.getById(id);
        if (byId != null){
            queryWrapper.eq(Command::getModelId,byId.getModelType());
            queryWrapper.eq(Command::getStatus,1);
            return commandMapper.selectList(queryWrapper);
        }else {
            return new ArrayList<>();
        }
    }

    @Override
    public List<Command> selectCommandByModel(String modelId) {
        LambdaQueryWrapper<Command>queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Command::getModelId,modelId);
        return commandMapper.selectList(queryWrapper);
    }

    @Override
    public List<ModelCommand> selectGroupCommandById(String modelId) {
        List<ModelCommand> list = new ArrayList<>();
        ModelCommand phoneCommand = new ModelCommand();
        ModelCommand adminCommand = new ModelCommand();

        List<Command> adminList = commandMapper.selectCommandByType(modelId,"admin");
        Map<String, List<Command>> adminCollect = adminList.stream().collect(Collectors.groupingBy(Command::getControlGroup));
        adminCommand.setType("admin").setCommands(adminCollect);
        list.add(adminCommand);

        List<Command> phoneList = commandMapper.selectCommandByType(modelId,"phone");
        Map<String, List<Command>> phoneCollect = phoneList.stream().collect(Collectors.groupingBy(Command::getControlGroup));
        phoneCommand.setType("phone").setCommands(phoneCollect);
        list.add(phoneCommand);
        return list;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void insertCommand(Command para) {
        para.setStatus(0);
        if (para.getCommandCode().equals("018801") || para.getCommandCode().equals("018800")){
            para.setCommandSign("81");
        }
        this.save(para);
        CommandLog commandLog = new CommandLog();
        commandLog.setCommandId(para.getId());
        commandLog.setModelId(para.getModelId());
        commandLog.setOperateType("添加指令");
        commandLog.setCommandName(para.getGroupName()+"-"+para.getCommandName());
        commandLogService.save(commandLog);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void insertCommandBatch(CommandBatch para) {
        List<Command> list = new ArrayList<Command>();
        List<CommandLog> logList = new ArrayList<CommandLog>();
        for (String modelId : para.getModelId()){
            Command command = new Command();
            command.setId(IdUtil.getSnowflakeNextIdStr());
            BeanUtils.copyProperties(para,command);
            command.setModelId(modelId);
            list.add(command);

            CommandLog commandLog = new CommandLog();
            commandLog.setCommandId(command.getId());
            commandLog.setModelId(modelId);
            commandLog.setOperateType("添加指令");
            commandLog.setCommandName(para.getGroupName()+"-"+para.getCommandName());
            logList.add(commandLog);
        }
        this.saveBatch(list);
        commandLogService.saveBatch(logList);
    }

    @Override
    public void updateCommand(CommandRequest para) {
        Command query = commandMapper.selectById(para.getId());
        if (query == null){
            throw new MyException("wds.exception.id");
        }
        Command command = new Command();
        command.setId(para.getId());
        command.setCommandCode(para.getCommandCode());
        if (para.getCommandCode().equals("018801") || para.getCommandCode().equals("018800")){
            command.setCommandSign("81");
        }
        commandMapper.updateById(command);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void updateCommandStatus(Command para) {
        Command query = commandMapper.selectById(para.getId());
        if (query == null){
            throw new MyException("wds.exception.id");
        }
        Command command = new Command();
        command.setId(para.getId());
        command.setStatus(para.getStatus());
        commandMapper.updateById(command);

        CommandLog commandLog = new CommandLog();
        commandLog.setCommandId(para.getId());
        commandLog.setModelId(query.getModelId());
        commandLog.setOperateType("变更指令");
        commandLog.setOldStatus(query.getStatus());
        commandLog.setNewStatus(para.getStatus());
        commandLog.setCommandName(para.getGroupName()+"-"+query.getCommandName());
        commandLogService.save(commandLog);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void deleteCommand(String id,String groupName) {
        Command query = commandMapper.selectById(id);
        if (query == null){
            throw new MyException("wds.exception.id");
        }
        if (query.getStatus()==1){
            throw new MyException("wds.exception.deleteCommand");
        }
        commandMapper.deleteById(id);
        CommandLog commandLog = new CommandLog();
        commandLog.setCommandId(id);
        commandLog.setModelId(query.getModelId());
        commandLog.setOperateType("删除指令");
        commandLog.setCommandName(groupName+"-"+query.getCommandName());
        commandLogService.save(commandLog);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void deleteControlGroup(DelGroupRequest para) {
        LambdaQueryWrapper<Command> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Command::getControlGroup,para.getControlGroup())
                .eq(Command::getModelId,para.getModelId())
                .eq(Command::getType,para.getType());
        List<Command> list = commandMapper.selectList(queryWrapper);
        list.stream().filter(command -> command.getStatus()==1)
                .findFirst()
                .ifPresent(command -> {
                    throw new MyException("wds.exception.deleteCommand");
                });
        commandMapper.delete(queryWrapper);

        CommandLog commandLog = new CommandLog();
        commandLog.setModelId(para.getModelId());
        commandLog.setOperateType("删除控车项");
        commandLog.setCommandName(para.getGroupName());
        commandLogService.save(commandLog);
    }

    @Override
//    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void issueCommand(IssueCommandByCode para) {
        if (!smsSwitch){
            mqNotice(para.getId(),para.getCommand());
        }else {
            MultiValueMap<String,String> param = new LinkedMultiValueMap<>();
            param.add("userId",LoginHolder.getLoginUser().getUid());
            JsonNode jsonNode = WebHttpApi.get("http://sys-permit/sys-permit/user-dept/getUserById", param);
            if (jsonNode.get("code").asInt() != 200) {
                throw new MyException("wds.exception");
            }
            JsonNode data = jsonNode.get("data");
            JsonNode mobileDc = data.get("mobileDc");
            String mobile = symmetricCrypto.decryptStr(mobileDc.asText());
            Object code = redisTemplate.opsForValue().get("CONTROL_VEHICLE:" + mobile);
            if (code == null){
                throw new MyException("wds.exception.verificationCode");
            }
            if (code.equals(para.getCode())){
                redisTemplate.delete("CONTROL_VEHICLE:" + mobile);
                //发送消息
                mqNotice(para.getId(),para.getCommand());
            }else {
                throw new MyException("wds.exception.verificationCode");
            }
        }
    }

    @Override
    public List<String> selectCommands() {
        return commandMapper.selectCommands();
    }

    //分开发送，单个
    public void mqNotice(String vinId,String id){
        VehicleSale byId = vehicleSaleService.getById(vinId);
        Command command = commandMapper.selectById(id);
        if (command == null){
            throw new MyException("wds.exception.id");
        }
        //下发指令时需要校验使能状态
        if ("82".equals(command.getCommandSign())){
            LambdaQueryWrapper<VehicleMessageDaily> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(VehicleMessageDaily::getVin, byId.getVin());
            List<VehicleMessageDaily> vehicleMessageDailies = vehicleMessageDailyMapper.selectList(wrapper);

            if (!vehicleMessageDailies.isEmpty()) {
                VehicleMessageDaily vehicleMessageDaily = vehicleMessageDailies.getFirst();
                LockEnableStatusEnum lockEnableStatus = LockEnableStatusEnum.fromValue(vehicleMessageDaily.getLockEnableStatus());
                if (lockEnableStatus == LockEnableStatusEnum.NOT_SUPPORTED) {
                    throw new MyException("wds.exception.lockEnableStatus");
                }
            }
        }
//        String msg = byId.getVin()+"-"+command.getCommandCode()+"-"+byId.getDid()+"-"+command.getCommandSign();
//        streamBridge.send("tspcommand-out-0", MessageBuilder.withPayload(msg).build());
        String tag = vehTagCache.get(byId.getVin());
        System.out.println("车辆状态："+tag);

        //是否是秘钥指令
        String hkey = command.getCommandCode();
        //判断是否是动态指令包含%s（需要三个字节码校验）
        if (command.getCommandCode().contains("%s")){
            String s = "";
            if (command.getFunctionCode() == 1){
                s = CheckSumUtil.processVin(byId.getVin(), (byte) 8);
                command.setCommandCode(command.getCommandCode().replace("%s",s));
            }else if (command.getFunctionCode() == 2){
                s = CheckSumUtil.processVin(byId.getVin(), (byte) 6);
                command.setCommandCode(command.getCommandCode().replace("%s",s));
            }
            hkey = s;
        }

        //指令
        CmdObj cmd1 = CmdObj.builder().cmd(command.getCommandCode())
                .vin(byId.getVin())
                .did(byId.getDid())
                .funCode(Byte.parseByte(command.getCommandSign()))
                .key(byId.getVin() + "-" + hkey)
                .build();
        log.info(cmd1.toString());



        Boolean b = redisTemplate.opsForHash().hasKey("VIN_CMD", byId.getVin() + "-" + hkey);
        redisTemplate.opsForHash().put("VIN_CMD",byId.getVin()+"-"+hkey,cmd1);

        /*Long index = redisTemplate.opsForList().indexOf("vinCmd", cmd1);
        //判断是否存在
        Boolean b = redisTemplate.opsForHash().hasKey("second-cmd", byId.getVin() +"-"+ command.getCommandSign());

        CmdObj cmd2 = null;*/
        //使能  合并发指令
        /*if (command.getCmdRelate() == CmdRelateEnum.BEFORE){
            cmd2 = CmdObj.builder().cmd("018801")
                    .vin(byId.getVin())
                    .did(byId.getDid())
                    .funCode(Byte.parseByte("81"))
                    .build();
            Long index2 = redisTemplate.opsForList().indexOf("vinCmd", cmd2);
            if (index2 == null){
                redisTemplate.opsForList().rightPush("vinCmd", cmd2);
            }
            redisTemplate.opsForHash().put("second-cmd",byId.getVin()+"-81",cmd1);
            if (StrUtil.isNotBlank(tag)){
                sendControl(tag, cmd2);
            }
        }else if (command.getCmdRelate() == CmdRelateEnum.AFTER){
            cmd2 = CmdObj.builder().cmd("018800")
                    .vin(byId.getVin())
                    .did(byId.getDid())
                    .funCode(Byte.parseByte("81"))
                    .build();
            Long index2 = redisTemplate.opsForList().indexOf("vinCmd", cmd2);
            if (index == null){
                redisTemplate.opsForList().rightPush("vinCmd", cmd1);
            }
            if (index2 == null){
                redisTemplate.opsForList().rightPush("vinCmd", cmd2);
            }
            //            redisTemplate.opsForHash().put("second-cmd",byId.getVin()+"-"+command.getCommandSign(),cmd2);
            if (StrUtil.isNotBlank(tag)){
                sendControl(tag, cmd1);
                sendControl(tag, cmd2);
            }
        }else {
            if (index == null){
                redisTemplate.opsForList().rightPush("vinCmd", cmd1);
            }
            if (StrUtil.isNotBlank(tag)){
                sendControl(tag, cmd1);
            }
        }*/
        // 单独发指令
        /*if (index == null){
            redisTemplate.opsForList().rightPush("vinCmd", cmd1);
        }*/
        if (StrUtil.isNotBlank(tag)){
            sendControl(tag, cmd1);
        }


//        CmdObj cmdHistory = vinCmdCache.get(byId.getVin());
//        Boolean member = redisTemplate.opsForSet().isMember("COMMAND2BOX", msg);

        //如果存在则使之前的指令历史改成失效，由于每次下发前
        if (b){
            CommandExecHistory execHistory = commandExecHistoryService.selectPendingCommand(byId.getVin(), id);
            if (execHistory != null){
                CommandExecHistory history = new CommandExecHistory();
                history.setId(execHistory.getId());
                history.setStatus(ExecuteStatus.CANCEL);
                commandExecHistoryService.updateById(history);
            }
        }

        CommandExecHistory commandExecHistory = new CommandExecHistory();
        commandExecHistory.setCommand(command.getId());
        commandExecHistory.setVin(byId.getVin());
        commandExecHistory.setStatus(ExecuteStatus.PENDING);
        String ip = ServletUtils.getClientIP();
        commandExecHistory.setIp(ip);
        commandExecHistoryService.save(commandExecHistory);

        /*if (StrUtil.isNotBlank(tag)){
            sendControl(tag, cmd1);
        }*/
//        redisTemplate.opsForHash().put("vin-cmd:"+byId.getVin(),msg);
//        System.out.println(msg);
    }

    // 合并发送
    public void mqNotice1(String vinId,String id){
        VehicleSale byId = vehicleSaleService.getById(vinId);
        Command command = commandMapper.selectById(id);
        if (command == null){
            throw new MyException("wds.exception.id");
        }
//        String msg = byId.getVin()+"-"+command.getCommandCode()+"-"+byId.getDid()+"-"+command.getCommandSign();
//        streamBridge.send("tspcommand-out-0", MessageBuilder.withPayload(msg).build());
        String tag = vehTagCache.get(byId.getVin());

        //判断是否是动态指令包含%s（需要三个字节码校验）
        if (command.getCommandCode().contains("%s")){
            String s = CheckSumUtil.processVin(byId.getVin(), command.getCmdRelate().getValue());
            command.setCommandCode(command.getCommandCode().replace("%s",s));
        }

        //指令
        CmdObj cmd1 = CmdObj.builder().cmd(command.getCommandCode())
                .vin(byId.getVin())
                .did(byId.getDid())
                .funCode(Byte.parseByte(command.getCommandSign()))
                .build();
        log.info(cmd1.toString());

        Long index = redisTemplate.opsForList().indexOf("vinCmd", cmd1);
        //判断是否存在
        Boolean b = redisTemplate.opsForHash().hasKey("second-cmd", byId.getVin() +"-"+ command.getCommandSign());

        CmdObj cmd2 = null;
        //使能
        if (command.getCmdRelate() == CmdRelateEnum.BEFORE){
            cmd2 = CmdObj.builder().cmd("018801")
                    .vin(byId.getVin())
                    .did(byId.getDid())
                    .funCode(Byte.parseByte("81"))
                    .build();
            Long index2 = redisTemplate.opsForList().indexOf("vinCmd", cmd2);
            if (index2 == null){
                redisTemplate.opsForList().rightPush("vinCmd", cmd2);
            }
            redisTemplate.opsForHash().put("second-cmd",byId.getVin()+"-81",cmd1);
            if (StrUtil.isNotBlank(tag)){
                sendControl(tag, cmd2);
            }
        }else if (command.getCmdRelate() == CmdRelateEnum.AFTER){
            cmd2 = CmdObj.builder().cmd("018800")
                    .vin(byId.getVin())
                    .did(byId.getDid())
                    .funCode(Byte.parseByte("81"))
                    .build();
            Long index2 = redisTemplate.opsForList().indexOf("vinCmd", cmd2);
            if (index == null){
                redisTemplate.opsForList().rightPush("vinCmd", cmd1);
            }
            if (index2 == null){
                redisTemplate.opsForList().rightPush("vinCmd", cmd2);
            }
            //            redisTemplate.opsForHash().put("second-cmd",byId.getVin()+"-"+command.getCommandSign(),cmd2);
            if (StrUtil.isNotBlank(tag)){
                sendControl(tag, cmd1);
                sendControl(tag, cmd2);
            }
        }else {
            if (index == null){
                redisTemplate.opsForList().rightPush("vinCmd", cmd1);
            }
            if (StrUtil.isNotBlank(tag)){
                sendControl(tag, cmd1);
            }
        }



//        CmdObj cmdHistory = vinCmdCache.get(byId.getVin());
//        Boolean member = redisTemplate.opsForSet().isMember("COMMAND2BOX", msg);

        //如果存在则使之前的指令历史改成失效，由于每次下发前
        if (index != null || b){
            CommandExecHistory execHistory = commandExecHistoryService.selectPendingCommand(byId.getVin(), id);
            if (execHistory != null){
                CommandExecHistory history = new CommandExecHistory();
                history.setId(execHistory.getId());
                history.setStatus(ExecuteStatus.CANCEL);
                commandExecHistoryService.updateById(history);
            }
        }

        CommandExecHistory commandExecHistory = new CommandExecHistory();
        commandExecHistory.setCommand(command.getId());
        commandExecHistory.setVin(byId.getVin());
        commandExecHistory.setStatus(ExecuteStatus.PENDING);
        String ip = ServletUtils.getClientIP();
        commandExecHistory.setIp(ip);
        commandExecHistoryService.save(commandExecHistory);

        /*if (StrUtil.isNotBlank(tag)){
            sendControl(tag, cmd1);
        }*/
//        redisTemplate.opsForHash().put("vin-cmd:"+byId.getVin(),msg);
//        System.out.println(msg);
    }


    private void sendControl(String tag, CmdObj cmd) {
        log.info(cmd.toString());
        try {
            RpcContext.getContext().setAttachment("dubbo.tag", tag);
            cmdDownService.cmdDown(cmd);
        }catch (Exception e){
            e.printStackTrace();
            //港哥要求不弹出提示信息
//            throw new MyException(e.getMessage());
        }
    }


}